package auth

import (
	"fmt"
	"path/filepath"
	"strings"
	"time"

	"baseService/internal/infrastructure/config"
	"baseService/internal/pkg/errors"
	"baseService/internal/pkg/logger"
	"baseService/internal/pkg/response"

	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
)

// Claims JWT声明结构
type Claims struct {
	UserID   uint   `json:"user_id"`
	Username string `json:"username"`
	Email    string `json:"email"`
	Role     string `json:"role"`
	jwt.RegisteredClaims
}

// JWTAuthMiddleware JWT认证中间件
type JWTAuthMiddleware struct {
	config *config.AuthMiddlewareConfig
	logger logger.Logger
}

// NewJWTAuthMiddleware 创建JWT认证中间件
func NewJWTAuthMiddleware(config *config.AuthMiddlewareConfig, logger logger.Logger) *JWTAuthMiddleware {
	return &JWTAuthMiddleware{
		config: config,
		logger: logger,
	}
}

// Handler 中间件处理函数
func (m *JWTAuthMiddleware) Handler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 如果中间件未启用，直接通过
		if !m.config.Enable {
			c.Next()
			return
		}

		// 检查是否在跳过路径中
		if m.shouldSkipPath(c.Request.URL.Path) {
			c.Next()
			return
		}

		// 获取JWT令牌
		token := m.extractToken(c)
		if token == "" {
			m.logger.Warnf("JWT令牌缺失 - path: %s, ip: %s", c.Request.URL.Path, c.ClientIP())
			response.UnauthorizedAccess(c)
			c.Abort()
			return
		}

		// 验证JWT令牌
		claims, err := m.validateToken(token)
		if err != nil {
			m.logger.Warnf("JWT令牌验证失败 - error: %s, path: %s, ip: %s", err.Error(), c.Request.URL.Path, c.ClientIP())

			// 根据错误类型返回不同响应
			if err.Error() == "token is expired" {
				response.TokenExpired(c)
			} else {
				response.UnauthorizedAccess(c)
			}
			c.Abort()
			return
		}

		// 检查是否需要刷新令牌
		if m.shouldRefreshToken(claims) {
			newToken, err := m.refreshToken(claims)
			if err != nil {
				m.logger.Errorf("令牌刷新失败 - error: %s, user_id: %d", err.Error(), claims.UserID)
			} else {
				// 在响应头中返回新令牌
				c.Header("X-New-Token", newToken)
				m.logger.Infof("令牌已刷新 - user_id: %d", claims.UserID)
			}
		}

		// 将用户信息设置到上下文中
		m.setUserContext(c, claims)

		// 记录认证成功日志
		m.logger.Debugf("JWT认证成功 - user_id: %d, username: %s, path: %s", claims.UserID, claims.Username, c.Request.URL.Path)

		c.Next()
	}
}

// shouldSkipPath 检查是否应该跳过认证
func (m *JWTAuthMiddleware) shouldSkipPath(path string) bool {
	for _, skipPath := range m.config.SkipPaths {
		// 支持通配符匹配
		if matched, _ := filepath.Match(skipPath, path); matched {
			return true
		}
		// 支持前缀匹配
		if strings.HasSuffix(skipPath, "*") {
			prefix := strings.TrimSuffix(skipPath, "*")
			if strings.HasPrefix(path, prefix) {
				return true
			}
		}
		// 精确匹配
		if path == skipPath {
			return true
		}
	}
	return false
}

// extractToken 从请求中提取JWT令牌
func (m *JWTAuthMiddleware) extractToken(c *gin.Context) string {
	// 1. 从Authorization头获取 (优先级最高)
	authHeader := c.GetHeader(m.config.TokenHeader)
	if authHeader != "" {
		// 支持 "Bearer token" 格式，固定使用 "Bearer " 前缀
		if strings.HasPrefix(authHeader, "Bearer ") {
			return strings.TrimPrefix(authHeader, "Bearer ")
		}
		// 如果没有Bearer前缀，直接返回整个header值
		return authHeader
	}

	// 2. 从查询参数获取
	if m.config.TokenQuery != "" {
		if token := c.Query(m.config.TokenQuery); token != "" {
			return token
		}
	}

	// 3. 从Cookie获取 (固定使用 access_token)
	if token, err := c.Cookie("access_token"); err == nil && token != "" {
		return token
	}

	return ""
}

// validateToken 验证JWT令牌
func (m *JWTAuthMiddleware) validateToken(tokenString string) (*Claims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, errors.NewAuthErrorWithMessage(errors.ErrInvalidToken)
		}
		return []byte(m.config.JWTSecret), nil
	})

	if err != nil {
		return nil, err
	}

	claims, ok := token.Claims.(*Claims)
	if !ok || !token.Valid {
		return nil, errors.NewAuthErrorWithMessage(errors.ErrInvalidToken)
	}

	// 检查令牌是否过期
	if claims.ExpiresAt != nil && claims.ExpiresAt.Before(time.Now()) {
		return nil, errors.NewAuthErrorWithMessage(errors.ErrTokenExpired)
	}

	// 检查发行者
	if claims.Issuer != m.config.Issuer {
		return nil, errors.NewAuthErrorWithMessage(errors.ErrInvalidToken)
	}

	return claims, nil
}

// shouldRefreshToken 检查是否应该刷新令牌
func (m *JWTAuthMiddleware) shouldRefreshToken(claims *Claims) bool {
	// 计算令牌剩余时间
	now := time.Now()
	if claims.ExpiresAt == nil {
		return false
	}
	remaining := claims.ExpiresAt.Sub(now)

	// 如果剩余时间小于刷新阈值，则需要刷新
	thresholdDuration := time.Duration(m.config.RefreshThreshold) * time.Minute
	return remaining < thresholdDuration && remaining > 0
}

// refreshToken 刷新JWT令牌
func (m *JWTAuthMiddleware) refreshToken(oldClaims *Claims) (string, error) {
	// 检查刷新次数限制
	refreshCount := 0
	if oldClaims.RegisteredClaims.Subject != "" {
		// 这里可以从数据库或缓存中获取用户的刷新次数
		// 简化实现，暂时不做限制检查
	}

	if refreshCount >= m.config.MaxRefreshCount {
		return "", errors.NewAuthErrorWithMessage(errors.ErrTokenExpired)
	}

	// 创建新的声明
	now := time.Now()
	expireDuration := time.Duration(m.config.TokenExpiry) * time.Second

	newClaims := &Claims{
		UserID:   oldClaims.UserID,
		Username: oldClaims.Username,
		Email:    oldClaims.Email,
		Role:     oldClaims.Role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(expireDuration)),
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			Issuer:    m.config.Issuer,
			Subject:   oldClaims.Subject,
		},
	}

	// 生成新令牌
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, newClaims)
	tokenString, err := token.SignedString([]byte(m.config.JWTSecret))
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

// setUserContext 设置用户信息到上下文
func (m *JWTAuthMiddleware) setUserContext(c *gin.Context, claims *Claims) {
	// 设置用户信息到上下文中，供后续中间件和处理器使用
	c.Set("user_id", claims.UserID)
	c.Set("username", claims.Username)
	c.Set("user_email", claims.Email)
	c.Set("user_role", claims.Role)
	c.Set("user_claims", claims)

	// 设置到Header中，方便其他服务使用
	c.Header("X-User-ID", fmt.Sprintf("%d", claims.UserID))
	c.Header("X-Username", claims.Username)
}

// GenerateToken 生成JWT令牌 (辅助函数，供登录使用)
func (m *JWTAuthMiddleware) GenerateToken(userID uint, username, email, role string) (string, error) {
	now := time.Now()
	expireDuration := time.Duration(m.config.TokenExpiry) * time.Second
	if expireDuration <= 0 {
		expireDuration = 24 * time.Hour // 默认24小时
	}

	claims := &Claims{
		UserID:   userID,
		Username: username,
		Email:    email,
		Role:     role,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(now.Add(expireDuration)),
			IssuedAt:  jwt.NewNumericDate(now),
			NotBefore: jwt.NewNumericDate(now),
			Issuer:    m.config.Issuer,
			Subject:   username,
		},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString([]byte(m.config.JWTSecret))
	if err != nil {
		return "", err
	}

	return tokenString, nil
}

// GetUserFromContext 从上下文获取用户信息 (辅助函数)
func GetUserFromContext(c *gin.Context) (*Claims, bool) {
	if claims, exists := c.Get("user_claims"); exists {
		if userClaims, ok := claims.(*Claims); ok {
			return userClaims, true
		}
	}
	return nil, false
}

// RequireRole 要求特定角色的中间件
func RequireRole(roles ...string) gin.HandlerFunc {
	return func(c *gin.Context) {
		claims, exists := GetUserFromContext(c)
		if !exists {
			response.UnauthorizedAccess(c)
			c.Abort()
			return
		}

		// 检查角色权限
		hasRole := false
		for _, role := range roles {
			if claims.Role == role {
				hasRole = true
				break
			}
		}

		if !hasRole {
			response.PermissionDenied(c)
			c.Abort()
			return
		}

		c.Next()
	}
}

// OptionalHandler OptionalAuth 可选认证中间件 (不强制要求认证，但如果有令牌会解析)
func (m *JWTAuthMiddleware) OptionalHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		// 尝试获取JWT令牌
		token := m.extractToken(c)
		if token == "" {
			c.Next()
			return
		}

		// 尝试验证JWT令牌
		claims, err := m.validateToken(token)
		if err != nil {
			// 令牌无效，但不阻止请求继续
			m.logger.Debugf("可选认证令牌验证失败 - error: %s", err.Error())
			c.Next()
			return
		}

		// 令牌有效，设置用户信息
		m.setUserContext(c, claims)
		c.Next()
	}
}
